package util.jackson.serializer;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import shared.model.ExtensionPoint;

import java.io.IOException;

/**
 * @author Gregory Boissinot
 */
public class JacksonReader {

    public <T> T readRequiredFieldValue(JsonNode rootNode, String fieldName, Class<T> type) throws IOException {
        return readFieldValue(rootNode, fieldName, true, type);
    }

    public <T> T readOptionalField(JsonNode rootNode, String fieldName, Class<T> type) throws IOException {

        if (fieldName == null) {
            return null;
        }

        return readFieldValue(rootNode, fieldName, false, type);
    }

    private <T> T readFieldValue(JsonNode rootNode, String fieldName, boolean mandatoryField, Class<T> typeClass) throws IOException {

        final JsonNode fieldNode = getFieldNode(rootNode, fieldName, mandatoryField);
        if (fieldNode == null) {
            return null;
        }

        if (fieldNode instanceof TextNode) {
            return (T) fieldNode.asText();
        }

        if (fieldNode instanceof ObjectNode) {
            final ObjectMapper objectMapper = ImplementationMapper.getObjectMapper();
            if (ExtensionPoint.class.isAssignableFrom(typeClass)) {
                final Class<? extends ExtensionPoint> implementedExtensionClass = ImplementationMapper.getDeserializerClassMapper().get(fieldName);
                return (T) objectMapper.readValue(fieldNode.toString(), implementedExtensionClass);
            }
            return objectMapper.readValue(fieldNode.toString(), typeClass);
        }

        if (fieldNode instanceof ArrayNode) {
            ObjectMapper objectMapper = new ObjectMapper();
            return (T) objectMapper.readValue(fieldNode.toString(), typeClass);
        }


        throw new InvalidJSONDocumentException(String.format("Not supported node type.", fieldName));
    }

    private JsonNode getFieldNode(JsonNode rootNode, String fieldName, boolean mandatoryField) {
        final JsonNode fieldNode = rootNode.get(fieldName);
        if (fieldNode == null && !mandatoryField) {
            return null;
        }

        if (fieldNode == null && mandatoryField) {
            throw new InvalidJSONDocumentException(String.format("%s is required.", fieldName));
        }

        return fieldNode;
    }

}
